#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project ：idi_mes 
@File    ：output_wip_newsn.py
@IDE     ：PyCharm 
@Author  ：孔令伟
@Date    ：9/19/24 9:38 AM 
"""
from copy import deepcopy
from typing import Union, Dict, List, Tuple

from mg_app_framework import MesCode, get_organization

from common.basic_mongo import get_retrospect_collection, get_mongo_collection
from common.constant import RetrospectType
from common.retrospect_operate.handle import filter_device_code_workorder_config
from prod_ctrl_service.tool.tool import check_basic_param, get_qrcode_latest_retros, make_retrospect_data_and_publish, \
    get_time, check_rule_code_exists, insert_prod_result_param, get_ng_name, send_unqualified_http, \
    insert_unqualified_records_backup, check_wip_no_exists


async def output_wip_newsn_check(params: Dict[str, Union[str, List[Dict[str, Union[str, int, List[Dict[str, str]]]]]]]) \
        -> Dict[str, Union[str, Dict]]:
    """

    :param params:{
                   "call_id":"JQ00386",  # 接口调用方认证ID,string,Y
                   "trace_id":"8941d16b-0581-4e68-8940-a651afff4282",  # 接口调用ID string,Y
                   "upperpc_code": "UP011",  # 上位机编码,string,Y
                   "user_code":"Y001",  # 员工编码  string,Y
                   "equip_code":"ZP001",  # 设备编码,string,Y
                   "process_code":"2050",  # 工序编码,string,Y
                   "wip_list":[            # 产出码列表,string,Y
                      {
                         "wip_no": "WW202408140001",  # 产出在制品唯一码,string,Y,负极电芯码或正极电芯码
                         "bind_wip_no":"WW202408140010",   # 绑定的进站码  string,Y,产出码为负极电芯码时传入绑定的卷芯收尾胶码；产出码为正极电芯码时传入绑定的负极电芯码
                         "is_ng": "0",               # 是否NG品,int,Y,0：OK，1：NG
                         "ng_code": [],               # NG代码,数组
                         "param_list": [             # 结果参数列表,数组
                            {
                               "param_code": "PM001",  # 参数编码,string,Y
                               "param_value": "0.95"   # 参数采集值,string,Y
                            },
                            {
                               "param_code": "PM002",
                               "param_value": "1"
                            }
                         ]
                      },
                      {
                         "wip_no": "WW202408140002",
                         "is_ng": "1",
                         "ng_code": ["E20001","E30002"],
                         "param_list": [
                            {
                               "param_code": "PM001",
                               "param_value": "0.95"
                            },
                            {
                               "param_code": "PM002",
                               "param_value": "1"
                            }
                         ]
                      }
                   ]
                }
    :return:
    """
    error_msg = ""
    # '接口调用方认证ID/接口调用ID/上位机编码/员工编码/设备编码'是每一个接口都有且必填的入参，检查这些参数是否填写
    basic_param_check_result, basic_param_check_message = check_basic_param(params=params)
    if not basic_param_check_result:
        error_msg += basic_param_check_message
    process_code = params.get("process_code")
    equip_code = params.get("equip_code")
    wip_list = params.get("wip_list", [])
    if not wip_list:
        error_msg += "请检查是否已填写'产出在制品唯一码参数列表(wip_list);"
    wip_no_list, bind_wip_no_list, wip_no_bind_wip_no_map = [], [], {}
    for wip_record in wip_list:
        wip_no = wip_record.get("wip_no", None)
        bind_wip_no = wip_record.get("bind_wip_no", None)
        is_ng = wip_record.get("is_ng", None)
        param_list: List[Dict[str, str]] = wip_record.get("param_list", [])
        if not wip_no or is_ng not in [1, 0] or not bind_wip_no:
            error_msg += "请检查是否已填写'产出在制品唯一码(wip_no)/是否NG品(is_ng)/绑定的进站码(bind_wip_no)';"
            break
        for rec in param_list:
            if not rec["param_code"] or not rec["param_value"]:
                error_msg += "请检查是否已完整填写'结果参数列表(param_list);"
                break
        wip_no_list.append(wip_no)
        bind_wip_no_list.append(bind_wip_no)
        wip_no_bind_wip_no_map.update({wip_no: bind_wip_no})
    if error_msg:
        result = {
            "code": MesCode.fail,  # 接口应答状态代码, success：成功; fail：失败, string
            "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
            "info": error_msg,
        }
        return result
    # 判断工序process_code是不是C100，如果是C100，需要修改成以下逻辑，对每一个进站码bind_wip_no做一个转换
    if process_code == "C100":
        check_rule_result, origin_code_new_code_map, check_rule_msg = await check_wip_no_exists(bind_wip_no_list)
        if not check_rule_result:
            error_msg += check_rule_msg
        else:
            bind_wip_no_list = list(origin_code_new_code_map.values())
    # 判断负极电芯码或正极电芯码是否在MES中已经生成
    no_exists_code_msg = await check_rule_code_exists(wip_no_list, wip_no_bind_wip_no_map)
    if no_exists_code_msg:
        error_msg += no_exists_code_msg
    # 检查bind_wip_no是否已经关联其它已经出站的电芯码
    bin_wip_no_legality_msg, bind_wip_no_workorder_code_map = await check_bind_wip_no_legality(
        bind_wip_no_list=bind_wip_no_list
    )
    if bin_wip_no_legality_msg:
        error_msg += bin_wip_no_legality_msg
    # 校验是否重复出站
    # qr_code_has_used = await check_repeat_in_or_out(
    #     procedure_code=process_code, qr_code_list=wip_no_list, op_type=RetrospectType.PRODUCT_OUT
    # )
    # if qr_code_has_used:
    #     error_msg += f"条码{qr_code_has_used}在工序{process_code}上已经有出站记录;"
    # 判断 绑定的进站码 码值最新的进出站操作是不是本工序进站
    latest_retros_check, latest_retros_msg = await get_qrcode_latest_retros(
        wip_no_list=bind_wip_no_list, op_type=RetrospectType.PRODUCT_IN, process_code=process_code
    )
    if not latest_retros_check:
        error_msg += latest_retros_msg
    # todo Marking校验 校验在制品是否标记Marking

    workorder_config = await filter_device_code_workorder_config(device_code=equip_code)
    bom_info = workorder_config.bom_info
    if not bom_info.product_out:
        error_msg += f"设备{equip_code}的BOM信息未配置完整;"
    if error_msg:
        result = {
            "code": MesCode.fail,  # 接口应答状态代码, success：成功; fail：失败, string
            "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
            "info": error_msg,
        }
        return result
    prod_result_param_list = []  # 根据入参构建的需要写入到出站参数表的数据
    unqualified_records: list = []  # 根据入参构建的需要写入到不合格品记录表的数据
    unqualified_records_backup: list = []  # 根据入参构建的需要写入到不合格品记录表的数据的备份
    # 追溯关系建立
    for wip_record in wip_list:
        sub_bind_wip_no = wip_record["bind_wip_no"]
        ng_list = [{"code": ng_code, "num": 1} for ng_code in wip_record.get("ng_code", [])]
        param = {
            "procedure_code": process_code,
            "device_code": equip_code,
            "is_ng": wip_record.get("is_ng"),
            "qr_code": wip_record.get("wip_no"),
            "ng_list": ng_list,
            "workorder_code": bind_wip_no_workorder_code_map.get(sub_bind_wip_no),
            # 使用bom产出物料第一个作为产出追溯的物料编码
            "material_code": bom_info.product_out[0].material_code
        }
        retros_result, retros_msg, input_lot_no_list = await make_retrospect_data_and_publish(param=param)
        if not retros_result:
            error_msg += retros_msg
        # 构建出站参数表数据
        prod_result_param_list.append({
            "call_id": params.get("call_id"),  # 接口调用方认证ID
            "trace_id": params.get("trace_id"),  # 接口调用ID
            "upperpc_code": params.get("upperpc_code"),  # 上位机编码
            "user_code": params.get("user_code"),  # 员工编码
            "equip_code": params.get("equip_code"),  # 设备编码
            "process_code": params.get("process_code"),  # 工序编码
            "wip_no": wip_record["wip_no"],
            "param_list": wip_record["param_list"],
            "time": get_time(),
            "data_source": "035"  # 此字段用于标记由哪个接口构建的数据
        })
        is_ng = wip_record['is_ng']
        if is_ng == 1:
            # 同一个码值的不合格品记录数目取决于有多少个ng-code
            for ng_item in ng_list:
                unqualified_reason_name = await get_ng_name(ng_item['code'])
                unqualified_data = {
                    "qr_code": wip_record["wip_no"],  # 码值
                    "product_code": bom_info.product_out[0].material_code,  # 不合格品物料编码
                    "product_name": bom_info.product_out[0].material_name,  # 不合格品物料名称
                    "process_code": workorder_config.procedure_code,  # 生产工序编码
                    "process_name": workorder_config.procedure_name,  # 生产工序名称
                    "device_code": workorder_config.device_code,  # 对应生产设备编码
                    "device_name": workorder_config.device_name,  # 对应生产设备编码
                    "production_date": get_time()[:10],  # 出库日期，格式为 %Y-%m-%d
                    "unqualified_reason_code": ng_item['code'],  # 不合格原因编码（对应不合格品基础配置中的不良品编码）
                    "unqualified_reason_name": unqualified_reason_name,  # 不合格原因（对应不合格品基础配置中的不良品名称）
                    "ok_qty": None,  # 合格数
                    "ng_qty": None,  # 不合格数
                    'user_name': params.get('user_code', '')
                }
                unqualified_records.append(unqualified_data)
                unqualified_data_backup = deepcopy(unqualified_data)
                unqualified_data_backup.update({"source": '035'})
                unqualified_records_backup.append(unqualified_data_backup)

    if error_msg:
        result = {
            "code": MesCode.fail,  # 接口应答状态代码, success：成功; fail：失败, string
            "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
            "info": error_msg,
        }
        return result
    # 写入出站参数
    if prod_result_param_list:
        await insert_prod_result_param(params=prod_result_param_list)
    # 将不合格品记录发送至不合格品app
    if unqualified_records:
        await send_unqualified_http(unqualified_records)
    # 本地将不合格品记录备份写入备份表
    if unqualified_records_backup:
        await insert_unqualified_records_backup(unqualified_records_backup)
    data = {
        "call_id": params.get("call_id"),  # 接口调用方认证ID, string
        "trace_id": params.get("trace_id"),  # 接口调用ID, string
        "upperpc_code": params.get("upperpc_code"),  # 上位机编码, string
        "user_code": params.get("user_code"),  # 员工编码, string
        "equip_code": params.get("equip_code"),  # 设备编码, string
        "process_code": params.get("process_code"),  # 工序编码, string
        "wip_list": params.get("wip_list"),
        "time": get_time()
    }
    await insert_data(data=data)
    result = {
        "code": MesCode.success,  # 接口应答状态代码, success：成功; fail：失败, string
        "data": {},  # 接口返回结果描述, json数组, 本接口不需要返回数据，返回null
        "info": "OK",  # 接口返回信息描述, 如果为fail, 则为fail的异常描述信息, string
    }
    return result


async def check_bind_wip_no_legality(bind_wip_no_list: list) \
        -> Tuple[str, Dict[str, str]]:
    """
    检查bind_wip_no是否已经关联其它已经出站的电芯码
    :param bind_wip_no_list:
    :return:
    """
    retros_coll = get_retrospect_collection()
    query_filter = {
        "qr_code": {"$in": bind_wip_no_list},
        "op_type": RetrospectType.PRODUCT_IN,
        "is_delete": False
    }
    workorder_cod_qr_code_map = {}
    bind_wip_no_workorder_code_map = {}
    async for rec in retros_coll.find(query_filter):
        workorder_code = rec["workorder_code"]
        qr_code = rec["qr_code"]
        workorder_cod_qr_code_map.update({
            workorder_code: qr_code
        })
        bind_wip_no_workorder_code_map.update({
            qr_code: workorder_code
        })

    workorder_code_filter = {
        'workorder_code': {"$in": list(workorder_cod_qr_code_map.keys())},
        "op_type": RetrospectType.PRODUCT_OUT
    }

    error_msg = ""
    async for rec in retros_coll.find(workorder_code_filter):
        workorder_code = rec["workorder_code"]
        qr_code = rec["qr_code"]
        bined_wip_no = workorder_cod_qr_code_map.get(workorder_code)

        error_msg += f"进站码{bined_wip_no}已绑定电芯码{qr_code}并成功出站;"

    return error_msg, bind_wip_no_workorder_code_map


async def insert_data(data):
    """
    登记按电芯提交电芯状态(OK, NG)
    :param data:
    :return:
    """
    collection = get_mongo_collection(
        database_name=f"{get_organization()}_mes",
        collection_name=f"output_wip_record",
    )
    await collection.insert_one(data)
